/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package mygame;

import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Spatial;
import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Texture2D;
import com.jme3.scene.Node;
import com.jme3.shader.VarType;

/**
 *
 * @author JhonKkk
 */
public class PassUtil {
    protected RenderManager renderManager;
    protected AssetManager assetManager;
    public static float[] S_BLUR_X = new float[]{0.001953125f, 0.0f};
    public static float[] S_BLUR_Y = new float[]{0.0f, 0.001953125f};
    public PassUtil(AssetManager assetManager, RenderManager renderManager){
        this.assetManager = assetManager;
        this.renderManager = renderManager;
    }
    public Geometry getUnitQuad()
    {
        float quadPos[] = {
            -1F, 1.0F, 0.0F, -1F, -1F, 0.0F, 1.0F, 1.0F, 0.0F, 1.0F, 
            -1F, 0.0F
        };
        float uvs[] = {
            0.0F, 1.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F, 0.0F
        };
        int indices[] = {
            0, 1, 2, 2, 1, 3
        };
        Mesh quadMesh = new Mesh();
        quadMesh.setBuffer(com.jme3.scene.VertexBuffer.Type.Position, 3, quadPos);
        quadMesh.setBuffer(com.jme3.scene.VertexBuffer.Type.TexCoord, 2, uvs);
        quadMesh.setBuffer(com.jme3.scene.VertexBuffer.Type.Index, 1, indices);
        quadMesh.updateBound();
        quadMesh.setStatic();
        Geometry quad = new Geometry("quadMesh", quadMesh);
        return quad;
    }
    public Texture2D genOffsetTexture2D(int w, int h){
        Texture2D offTex = new Texture2D(w, h, com.jme3.texture.Image.Format.RGBA32F);
        offTex.setMinFilter(com.jme3.texture.Texture.MinFilter.BilinearNearestMipMap);
        offTex.setMagFilter(com.jme3.texture.Texture.MagFilter.Bilinear);
        return offTex;
    }
    public Texture2D genTexture2D(Material material, int width, int height, int offsetId, Texture2D offTex)
    {
        Geometry quad = getUnitQuad();
        quad.setMaterial(material);
        createOffScreen(quad, offTex.getImage().getWidth(), offTex.getImage().getHeight(), offsetId, new Vector3f(0.0F, 0.0F, 10F), Vector3f.ZERO, Vector3f.UNIT_Y, offTex);
        quad.updateGeometricState();
        offTex.setMagFilter(com.jme3.texture.Texture.MagFilter.Bilinear);
        offTex.setMinFilter(com.jme3.texture.Texture.MinFilter.BilinearNoMipMaps);
        offTex.setAnisotropicFilter(0);
        offTex.setWrap(com.jme3.texture.Texture.WrapMode.EdgeClamp);
        return offTex;
    }
    public Texture2D genTexture2D2(Material material, Camera cam, int width, int height, int offsetId, Texture2D offTex)
    {
        Geometry quad = getUnitQuad();
        quad.setMaterial(material);
        quad.setCullHint(Spatial.CullHint.Never);
        createOffScreen(quad, width, height, offsetId, cam, offTex);
        quad.updateGeometricState();
        offTex.setMagFilter(com.jme3.texture.Texture.MagFilter.Bilinear);
        offTex.setMinFilter(com.jme3.texture.Texture.MinFilter.BilinearNoMipMaps);
        offTex.setAnisotropicFilter(0);
        offTex.setWrap(com.jme3.texture.Texture.WrapMode.EdgeClamp);
        return offTex;
    }
    public Texture2D genTexture2D(Spatial scene, Camera camera, int offsetId, Texture2D offTex)
    {
        createOffScreen(scene, offTex.getImage().getWidth(), offTex.getImage().getHeight(), offsetId, camera, offTex);
        scene.updateGeometricState();
        offTex.setMagFilter(com.jme3.texture.Texture.MagFilter.Bilinear);
        offTex.setMinFilter(com.jme3.texture.Texture.MinFilter.NearestLinearMipMap);
        offTex.setAnisotropicFilter(0);
        offTex.setWrap(com.jme3.texture.Texture.WrapMode.EdgeClamp);
        return offTex;
    }
    public Texture2D genRenderSceneTexture2D(Node scene, String sssNodeId, Material sssMaterial, Camera cam, int w, int h, int offsetId){
        Texture2D renderTexture2D = genOffsetTexture2D(w, h);
        Node cloneScene = (Node) scene.clone();
        Material skinPass0 = sssMaterial;
        skinPass0.selectTechnique("SkinUV", renderManager);
        cloneScene.getChild(sssNodeId).setMaterial(skinPass0);
        genTexture2D(cloneScene, cam, offsetId, renderTexture2D);
        return renderTexture2D;
    }
    public Texture2D genBlur(int vid, int hid, int w, int h, Material vblurMaterial, Material hblurMaterial){
        Texture2D vblur = genOffsetTexture2D(w, h);
        Texture2D hblur = genOffsetTexture2D(w, h);
        hblur = genTexture2D(hblurMaterial, w, h, hid, hblur);
        vblurMaterial.setTexture("Texture", hblur);
        vblur = genTexture2D(vblurMaterial, w, h, vid, vblur);
        return vblur;
    }
    public Texture2D genConvolutionTexture2D(Camera cam, Texture2D inputTexture2D, int xId, int yId, int outW, int outH, int strength, int kernelSize, int sigma, int resolution){
        Texture2D t = null;
        //x方向卷积
        Texture2D convolutionX = genOffsetTexture2D(resolution, resolution);
        //y方向卷积
        Texture2D convolutionY = genOffsetTexture2D(resolution, resolution);
        //创建卷积着色材质
        Material convolutionXMaterial = assetManager.loadMaterial("Materials/ConvolutionXMaterial.j3m");
        Material convolutionYMaterial = assetManager.loadMaterial("Materials/ConvolutionYMaterial.j3m");
        float[] CKernel = markKernel(sigma);
        System.out.println("长度:" + CKernel.length);
        convolutionXMaterial.setParam("CKernel", VarType.FloatArray, CKernel);
        convolutionXMaterial.setFloat("KERNEL_SIZE_FLOAT", kernelSize);
        convolutionXMaterial.setInt("KERNEL_SIZE_INT", kernelSize);
        convolutionXMaterial.setTexture("DiffuseMap", inputTexture2D);
        
        convolutionYMaterial.setParam("CKernel", VarType.FloatArray, CKernel);
        convolutionYMaterial.setFloat("KERNEL_SIZE_FLOAT", kernelSize);
        convolutionYMaterial.setInt("KERNEL_SIZE_INT", kernelSize);
        convolutionYMaterial.setTexture("DiffuseMap", convolutionX);
        genTexture2D(convolutionXMaterial, outW, outH, xId, convolutionX);
        genTexture2D(convolutionYMaterial, outW, outH, yId, convolutionY);
        return convolutionY;
    }
    private double gauss(float x, int sigma){
        return Math.exp(- ( x * x ) / ( 2.0 * sigma * sigma ));
    }
    public float[] markKernel(int sigma){
        int i = 0;
        float[] values = null;
        float sum = 0, halfWidth = 0;
        int kMaxKernelSize = 25;
        float kernelSize = (float) (2 * Math.ceil( sigma * 3.0 ) + 1);
        if ( kernelSize > kMaxKernelSize ) kernelSize = kMaxKernelSize;
        halfWidth = (float) (( kernelSize - 1 ) * 0.5);
        values = new float[(int)(kernelSize)];
        for ( i = 0; i < (int)(kernelSize); ++ i ) {

            values[ i ] = (float) gauss( i - halfWidth, sigma );
            sum += values[ i ];

        }
        //归一化kernel阵列
        for ( i = 0; i < kernelSize; ++ i ) values[ i ] /= sum;
        return values;
    }
    public void createOffScreen(Spatial rendererScene, int fbWidth, int fbHeight, int offsetId, Camera cam, 
            Texture2D texture2D)
    {
        Camera offCamera = cam;
        ViewPort offView = renderManager.createPreView((new StringBuilder()).append("Offscreen View").append(offsetId).toString(), offCamera);
        offView.setClearFlags(true, true, true);
        offView.setBackgroundColor(ColorRGBA.BlackNoAlpha);
        FrameBuffer offBuffer = new FrameBuffer(fbWidth, fbHeight, 1);
        offBuffer.setDepthBuffer(com.jme3.texture.Image.Format.Depth24Stencil8);
        offBuffer.setColorTexture(texture2D);
        offView.setOutputFrameBuffer(offBuffer);
        offView.attachScene(rendererScene);
        rendererScene.updateGeometricState();
    }
    public void createOffScreen(Spatial rendererScene, int fbWidth, int fbHeight, int offsetId, Vector3f eye, Vector3f at, Vector3f up, 
            Texture2D texture2D)
    {
        Camera offCamera = new Camera(fbWidth, fbHeight);
        ViewPort offView = renderManager.createPreView((new StringBuilder()).append("Offscreen View").append(offsetId).toString(), offCamera);
        offView.setClearFlags(true, true, true);
        offView.setBackgroundColor(ColorRGBA.BlackNoAlpha);
        offCamera.setFrustumPerspective(90F, 1.0F, 1.0F, 10F);
        offCamera.setLocation(eye);
        offCamera.lookAt(at, up);
        FrameBuffer offBuffer = new FrameBuffer(fbWidth, fbHeight, 1);
        offBuffer.setDepthBuffer(com.jme3.texture.Image.Format.Depth);
        offBuffer.setColorTexture(texture2D);
        offView.setOutputFrameBuffer(offBuffer);
        offView.attachScene(rendererScene);
        rendererScene.updateGeometricState();
    }
}
